package pt.inesc.id.l2f.annotation.unit;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import java.util.Collections;
import java.util.List;
import java.util.UUID;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;

import pt.inesc.id.l2f.annotation.tool.Tool;
import pt.inesc.id.l2f.annotation.tool.ToolVisitor;
import pt.inesc.id.l2f.annotation.tool.execution.ToolExecutionModeUnit;
import pt.inesc.id.l2f.annotation.document.laf.LinguisticAnnotationDocument;
import pt.inesc.id.l2f.annotation.document.laf.MorphoSyntacticAnnotation;
import pt.inesc.id.l2f.annotation.document.laf.Segmentation;
import pt.inesc.id.l2f.annotation.document.xml.XMLReader;
import pt.inesc.id.l2f.annotation.document.xml.XMLWriter;

public class LinguisticAnnotationProcessUnit extends ProcessUnit implements Comparable<LinguisticAnnotationProcessUnit> {
	// id generated by UUID class
	private Text _id;
	// annotation commond id (generated by UUID class)
	private Text _annotation;
	// stage counter
	private IntWritable _stage;
	// unit annotation dependencies
	private Dependencies _dependencies;
	// ...
	private LinguisticAnnotationDocument _input;
	// ...
	private LinguisticAnnotationDocument _output;
	// ... (result of documents merge)
	private LinguisticAnnotationDocument _document;

	public LinguisticAnnotationProcessUnit() {
		_dependencies = new Dependencies();
		_input = new LinguisticAnnotationDocument();
		_output = new LinguisticAnnotationDocument();
		_document = new LinguisticAnnotationDocument();
		
		// generate unique identifier
		_id = new Text(UUID.randomUUID().toString());
		_annotation = new Text(UUID.randomUUID().toString());
		_stage = new IntWritable(1);
	}
	
	public LinguisticAnnotationProcessUnit(String id, String annotation, int stage) {
		_dependencies = new Dependencies();
		_input = new LinguisticAnnotationDocument();
		_output = new LinguisticAnnotationDocument();
		_document = new LinguisticAnnotationDocument();
		_id = new Text(id);
		_annotation = new Text(annotation);
		_stage = new IntWritable(stage);
	}
	
	public LinguisticAnnotationProcessUnit(LinguisticAnnotationDocument input, LinguisticAnnotationDocument output, Dependencies dependencies, String annotation, int stage) {
		_input = input;
		_output = output;
		_dependencies = dependencies;
		
		_annotation = new Text(annotation);
		_stage = new IntWritable(stage);
		
		// merge input and output document
		_document = new LinguisticAnnotationDocument();
		_document.merge(input);
		_document.merge(output);
		
		// generate unique identifier
		_id = new Text(UUID.randomUUID().toString());
	}
	
	public LinguisticAnnotationProcessUnit(LinguisticAnnotationDocument input, LinguisticAnnotationDocument output) {
		this(input, output, new Dependencies(), UUID.randomUUID().toString(), 1);
	}

	@Override
	public void accept(ToolVisitor visitor) {
		visitor.visit(this);
	}
	
	@Override
	public void execute(Tool tool, ToolExecutionModeUnit unit) {
		tool.process(this, unit);
	}

	/**
	 * 
	 * 
	 * @param segmentation
	 */
	public void addSegmentation(Segmentation segmentation) {
		_output.addSegmentation(segmentation);
		_document.addSegmentation(segmentation);
	}
	
	/**
	 * 
	 * 
	 * @param annotation
	 */
	public void addMorphoSyntacticAnnotation(MorphoSyntacticAnnotation annotation) {
		_output.addMorphoSyntacticAnnotation(annotation);
		_document.addMorphoSyntacticAnnotation(annotation);
	}
	
	/**
	 * 
	 * 
	 * @return
	 */
	public List<Segmentation> getOutputDocumentSegmentations() {
		return Collections.unmodifiableList(_output.getSegmentations());
	}
	
	/**
	 * 
	 * 
	 * @return
	 */
	public List<MorphoSyntacticAnnotation> getOutputDocumentMorphoSyntacticAnnotations() {
		return Collections.unmodifiableList(_output.getMorphoSyntacticAnnotations());
	}
	
	/**
	 * 
	 * 
	 * @return
	 */
	public LinguisticAnnotationDocument getInputDocument() {
		return _input;
	}
	
	/**
	 * 
	 * 
	 * @return
	 */
	public LinguisticAnnotationDocument getDocument() {
		return _document;
	}
	
	/**
	 * 
	 * 
	 * @param odoc
	 */
	public void setDocument(LinguisticAnnotationDocument odoc) {
		_input = odoc;
	}
	
	/**
	 * 
	 * 
	 * @param output
	 */
	public void mergeOutput(LinguisticAnnotationDocument output) {
		_output.merge(output);
	}
	
	/**
	 * 
	 * 
	 * @param id
	 */
	public void addDependency(String id) {
		_dependencies.addDependency(id);
	}

	/**
	 * 
	 * 
	 * @return the dependencies
	 */
	public List<String> getDependencies() {
		return _dependencies.getDependencies();
	}

	/**
	 * 
	 * 
	 * @return the _annotation
	 */
	public String getAnnotationId() {
		return _annotation.toString();
	}
	
	/**
	 * 
	 * 
	 * @return the stage
	 */
	public int getStageNumber() {
		return _stage.get();
	}
	
	/**
	 * 
	 * 
	 * @return the id
	 */
	public String getId() {
		return _id.toString();
	}
	
	@Override
	public void readFrom(XMLReader xmlr) {
		int event = -1;
		
		while (true) {
			event = xmlr.next();
			
			if (xmlr.isElementEnd(event, "unit")) {
				break;
			}
			
			if (xmlr.isElementStart(event)) {
				String name = xmlr.getElementName();
				
				if (name.equals("dependencies")) {
					Dependencies dependencies = new Dependencies();
					dependencies.readFrom(xmlr);
					
					_dependencies = dependencies;
				}
				
				if (name.equals("laf")) {
					LinguisticAnnotationDocument doc = new LinguisticAnnotationDocument();
					doc.readFrom(xmlr);
					
					_input =  doc;
					_document = doc;
				}
			}
		}
	}
	
	@Override
	public void writeTo(XMLWriter xmlw) {
		xmlw.writeStartElement("unit");
		
		xmlw.writeAttribute("id", _id.toString());
		xmlw.writeAttribute("annotation", _annotation.toString());
		xmlw.writeAttribute("stage", _stage.toString());
		
		xmlw.writeStartElement("header");
		
		_dependencies.writeTo(xmlw);
		
		xmlw.writeEndElement();
		
		// write annotation
		_output.writeTo(xmlw);
		
		xmlw.writeEndElement();
	}

	public void readFields(DataInput in) throws IOException {
		_id = new Text();
		_id.readFields(in);
		
		_annotation = new Text();
		_annotation.readFields(in);
		
		_stage = new IntWritable();
		_stage.readFields(in);
		
		_dependencies = new Dependencies();
		_dependencies.readFields(in);
		
		_input = new LinguisticAnnotationDocument();
		_input.readFields(in);
		
		_output = new LinguisticAnnotationDocument();
		_output.readFields(in);
		
		_document = new LinguisticAnnotationDocument();
		_document.merge(_input);
		_document.merge(_output);
	}

	public void write(DataOutput out) throws IOException {
		_id.write(out);
		_annotation.write(out);
		_stage.write(out);
		_dependencies.write(out);
		_input.write(out);
		_output.write(out);
	}

	public int compareTo(LinguisticAnnotationProcessUnit unit) {
		return new Integer(_stage.get()).compareTo(unit.getStageNumber());
	}
}
